package pathfinder;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
import java.util.StringTokenizer;

public class Factory extends Thread {

  public ArrayList<Machine> typeofmachine;
  ArrayList<Machine> machines;
  ArrayList<String> cromosome;
  ArrayList<Chromosome> cromoFit;
  File file;
  //S = jumlah profit yang harus dilewati
  //H = jumlah jam kerja
  //P = jumlah profit sekarang
  //N = jumlah maksimum mesin
  //M = jumlah jenis mesin
  int S, H, P, N, M, algorithm;
  public Truck truk;
  public Scheduler bestSchedule;
  public boolean done ;

  public Factory() {
    typeofmachine = new ArrayList<>();
    machines = new ArrayList<>();
    cromosome = new ArrayList<>();
    cromoFit = new ArrayList<>();
    N = 10;
    algorithm = 0;
  }

  public Factory(File f, int algo) {
    this();
    try {
      file = f;
      BufferedReader br = new BufferedReader(new FileReader(f));
      StringTokenizer st = new StringTokenizer(br.readLine());
      S = Integer.parseInt(st.nextToken());
      H = Integer.parseInt(st.nextToken());
      M = Integer.parseInt(st.nextToken());
      N = Integer.parseInt(st.nextToken());
      truk = new Truck();
      truk.YCur = truk.Y = Integer.parseInt(st.nextToken());
      for (int x = 0; x < M; x++) {
        Machine m = new Machine();
        st = new StringTokenizer(br.readLine());
        m.T = Integer.parseInt(st.nextToken());
        m.W = Integer.parseInt(st.nextToken());
        m.S = Integer.parseInt(st.nextToken());
        m.P = Integer.parseInt(st.nextToken());
        m.I = Integer.parseInt(st.nextToken());
        m.C = Integer.parseInt(st.nextToken());
        m.R = Integer.parseInt(st.nextToken());
        m.D = Integer.parseInt(st.nextToken());
        typeofmachine.add(m);
      }
      st = new StringTokenizer(br.readLine());
      truk.N = Integer.parseInt(st.nextToken());
      for (int x = 0; x < truk.N; x++) {
        Vendor v = new Vendor(br.readLine());
        st = new StringTokenizer(br.readLine());
        int number = Integer.parseInt(st.nextToken());
        for (int y = 0; y < number; y++) {
          v.machine.add(Integer.parseInt(st.nextToken()));
        }
        truk.vendores.add(v);
      }

      for (int x = 0; x < M; x++) {
        st = new StringTokenizer(br.readLine());
        typeofmachine.get(x).productPrice = Integer.parseInt(st.nextToken());
      }
      st = new StringTokenizer(br.readLine());
      int height = Integer.parseInt(st.nextToken()), width = Integer.parseInt(st.nextToken());
      truk.map = new char[height][width];
      for (int x = 0; x < height; x++) {
        truk.map[x] = br.readLine().toCharArray();
      }
      truk.setVendores();
      algorithm = algo;
    } catch (IOException | NumberFormatException e) {
      System.out.println(e);
    }
  }

  // To randomly generated first population 
  public void generateRandomChromosome(int number) {
    cromosome = new ArrayList<>();
    StringBuilder sb = new StringBuilder();
    Random r = new Random();
    for (int i = 0; i < number; ++i) {
      for (int j = 0; j < H - 1; ++j) {
        int idx = r.nextInt(M) + 1;
        sb.append(idx);
        sb.append(' ');
      }
      cromosome.add(sb.toString());
      sb.delete(0, sb.length());
    }
  }

  // crossover the chromosome belom ada mutasi
  public String crossOver(String s1, String s2) {
    // assumption s1.length() == s2.length()
    StringBuilder sb = new StringBuilder();
    Random r = new Random();
    
    List<String> l1 = new ArrayList<>();
    List<String> l2 = new ArrayList<>();
    l1 = Arrays.asList(s1.split("\\s+"));
    l2 = Arrays.asList(s2.split("\\s+"));
    
    int idx = r.nextInt(l1.size() - 1);
    while(idx == 0) idx = r.nextInt(l1.size() - 1);
    
    for(int i = 0; i < idx; ++i) {
      sb.append(l1.get(i));
      sb.append(' ');
    }
    for(int i = idx; i < l1.size(); ++i) {
      sb.append(l2.get(i));
      sb.append(' ');
    }
  
    return sb.toString();
  }

  // mutasi
  public String mutate(String s) {
    StringBuilder sb = new StringBuilder();
    Random r = new Random();
    List<String> chromosomeArray = new ArrayList<>();
    chromosomeArray = Arrays.asList(s.split("\\s+"));

    int idx = r.nextInt(chromosomeArray.size());
    Integer newValue = r.nextInt(M) + 1;
    chromosomeArray.set(idx, newValue.toString());

    for (int i = 0; i < chromosomeArray.size(); ++i) {
      sb.append(chromosomeArray.get(i));
      if (i != chromosomeArray.size() - 1) {
        sb.append(' ');
      }
    }
    
    return sb.toString();
  }

  // Print all the string population
  public void printChromosome() {
    for (int x = 0; x < cromosome.size(); x++) {
//      System.out.println(cromosome.get(x));
    }
  }

  private static class fitnessComparator implements Comparator<Chromosome> {

    @Override
    public int compare(Chromosome o1, Chromosome o2) {
      return (o1.fitness > o2.fitness ? -1 : (o1.fitness == o2.fitness ? 0 : 1));
    }
  }

  //multithreading in genetic algorithm
  @Override
  public void run() {

    generateRandomChromosome(1000);
//    cromosome.add("1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1");
//    cromosome.add("1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2");
//    cromosome.add("1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 1 1 1 1");
//    cromosome.add("1 1 3 3 1 1 3 3 1 3 3 5 5 5 2 2 5 5 5");
//    cromosome.add("3 5 5 1 4 5 3 4 2 5 5 5 1 1 4 1 5 3 4");

    Random r = new Random();
    bestSchedule = new Scheduler(this, cromosome.get(0), S, 2);
//    System.out.println("INITIAL PROFIT = " + bestSchedule.profit);
    cromoFit = new ArrayList<>(); // pair <cromosome, fitness>
    done = false;
    final int GA_LIMIT = 50000;
    int iteration = 0;

    while (!done) {
      // sort cromosome berdasar fitness function
      
      for (int i = 0; i < cromosome.size(); ++i) {
        Factory f = new Factory(file, algorithm);
        Scheduler sc = new Scheduler(f, cromosome.get(i), S, algorithm);
        sc.calculateProfit();
//        System.out.println("SC PROFIT = " + sc.profit);
        if (sc.possible) {
//          System.out.println("POOOOOOOOOOOOOOOOOOOOOOOOOS");
          if (sc.profit > bestSchedule.profit) {
            bestSchedule = sc;
//            System.out.println(126378126);
            bestSchedule.path = f.truk.result;
          }
//          if(sc.profit > 50000) {
//            done = true;
//          }
          cromoFit.add(new Chromosome(cromosome.get(i), sc.profit));
        } else {
//          System.out.println("IIIIIIIIIIIIIIIIIIIIMPPPOSSSS");
//          System.out.println("CURP = " + sc.factory.typeofmachine.get(0).curP);
//          System.out.println(sc.profit);
//          for (int j = 0; j < f.typeofmachine.size(); ++j) {
//            System.out.println(f.typeofmachine.get(j).curP);
//          }
        }
      }

      Collections.sort(cromoFit, new fitnessComparator()); // sort desc by fitness
//      System.out.println("cromoFIT SIZE = " + cromoFit.size());
      ArrayList<String> newPopulation = new ArrayList<>();
      for (int i = 0; i < cromoFit.size() - 1; ++i) { // iterasi jadwal yang mungkin
        String anak = crossOver(cromoFit.get(i).s, cromoFit.get(i + 1).s);
        if (r.nextInt(20) == 0) { // mutasi dengan kemungkinan 5 persen
          anak = mutate(anak);
        }
        newPopulation.add(anak);
      }

      cromosome = newPopulation;
      if (cromosome.isEmpty() || iteration >= GA_LIMIT) {
        done = true;
      }
    }
  }

//  public static void main(String[] args) {
//    Factory f = new Factory(new File("input.txt"));
//    f.run();
////    System.out.println("CROSSOVER = " + f.crossOver("1 2 3 4 5","7 7 7 7 7"));
//    System.out.println("BEST PROFIT = " + f.bestSchedule.profit);
//    System.out.println("BEST SCHEDULE ");
//    System.out.println(f.bestSchedule.chromosomeArray);
//    System.out.println(f.bestSchedule.time.get(0));
//    System.out.println(f.bestSchedule.resTime);
//    System.out.println("PRODUCT CREATED");
//    System.out.println(f.bestSchedule.products);
    
//    f = new Factory(new File("input.txt"));
//    Scheduler s = new Scheduler(f, "1 1 3 3 1 1 3 3 1 3 3 5 5 5 2 2 5 5", 100000000, 2);
//    s.calculateProfit();
//    s.print();
//    System.out.println(s.profit);
//    for (int i = 0; i < f.typeofmachine.size(); ++i) {
//      System.out.println(f.typeofmachine.get(i).curP);
//    }
//
//    f = new Factory(new File("input.txt"));
//    s = new Scheduler(f, "1 1 3 3 1 1 3 3 1 3 3 5 5 5 2 2 5 5", 100000000, 2);
//    s.calculateProfit();
//    s.print();
//    System.out.println(s.profit);
//    for (int i = 0; i < f.typeofmachine.size(); ++i) {
//      System.out.println(f.typeofmachine.get(i).curP);
//    }

//    System.out.println(f.truk.aStar(2, 4, 0, 0));
//    HashSet<String> h= new HashSet<String>();
//    h.add("1");
//    if (h.contains("1")) {
//      System.out.println("YES");
//    }
//  }
}
